﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Model
{
    public class Tijd : Algoritme
    {
        private object stop;

        private static Tijd algoritme;

        private Tijd()
        {
            stop = true;
        }

        public Winkelwagen antwoord;

        public static Tijd GetInstance()
        {
            if (algoritme == null)
            {
                algoritme = new Tijd();
            }
            return algoritme;
        }

        public override void StopAlgoritme()
        {
            lock (stop)
            {
                stop = !((bool)stop);
            }
        }

        public override Winkelwagen AlgoritmeUitvoeren()
        {
            return VolgInstructies();
        }

        /// <summary>
        /// Dit deel voert het voorgestelde algoritme van meneer Beumers/Bemelmans uit
        /// </summary>
        /// <returns></returns>
        private Winkelwagen VolgInstructies()
        {
            this.antwoord = new Winkelwagen(LosOp());
            return antwoord;
        }


        private Winkelwagen LosOp()
        {
            Winkel w = Winkel.GetInstance();
            stop = false;
            List<Oplossing> S = new List<Oplossing>();
            S.Add(new Oplossing()); //voegt een lege oplossing toe, deze oplossing is het begin
            Oplossing h = new Oplossing();
            while (!(bool)stop && S.Count != 0)
            {
                Oplossing k = new Oplossing(S[0].Toestand, new Winkelwagen(S[0].Kar));
                S.RemoveAt(0);
                if (k.Kar.Totaalprijs > h.Kar.Totaalprijs )
                {
                    h = new Oplossing(k.Toestand, new Winkelwagen(k.Kar));
                    w.OplossingKlaar();
                }
                S.InsertRange(0, k.kinderen());
            }
            w.HelemaalKlaar();
            return h.Kar;
        }



        private class Oplossing
        {
            private List<POI> toestand;
            private Winkelwagen kar;

            public Oplossing()
            {
                toestand = new List<POI>();
                Toestand.AddRange(AbstracteLaag.GetInstance().PuntenVanInteresse);
                Kar = new Winkelwagen(new POI(new Point(AbstracteLaag.GetInstance().PuntenVanInteresse[0].Plaats.X, AbstracteLaag.GetInstance().PuntenVanInteresse[0].Plaats.Y), 0));
                foreach (Kind k in AbstracteLaag.GetInstance().PuntenVanInteresse[0].Kinderen)
                {
                    Kar.Plaats.VoegKindToe(new Kind(k.Nummer, k.Afstand));
                }
                Toestand[0].Doorlopen = false;
            }

            public Oplossing(List<POI> toestand, Winkelwagen kar)
            {
                this.toestand = new List<POI>();
                this.Toestand.AddRange(toestand);
                this.kar = new Winkelwagen(kar);
            }

            public List<Oplossing> kinderen()
            {
                //Filtering moet plaatsvinden
                //Neem alle artikelen
                //Neem alle paden



                List<Oplossing> antwoord = new List<Oplossing>();
                //maak een poi met alle eigenschappen van zijn origineel
                POI poi = new POI(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Plaats, AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Nummer);
                for (int i = 0; i < AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen.Count; i++)
                {
                    if (!(AbstracteLaag.GetInstance().Categoriën.Contains(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen[i].Naam)))
                    {
                        poi.Artikelen.Add(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen[i]);
                    }
                }
                //haal hier alle ongewenste producten uit
                for (int i = poi.Artikelen.Count - 1; i >= 0; i--)
                {
                    if (!AbstracteLaag.Instance.Categoriën.Contains(poi.Artikelen[i].Categorie))
                    {
                        poi.Artikelen.RemoveAt(i);
                    }
                }

                int aantal = poi.Artikelen.Count;
                //als winkelwagen vol is, de maximale afstand gelopen is
                if (!kar.Vol && (kar.Afstand <= AbstracteLaag.GetInstance().AantalMeter))
                {
                    List<Winkelwagen> wagens = new List<Winkelwagen>();
                    wagens.Add(new Winkelwagen(kar));
                    if (!kar.Plaats.Doorlopen && aantal > 0)
                    {
                        while (aantal>0 && !wagens[0].Vol)
                        {
                            wagens[0].LaadArtikelIn(poi.Artikelen[aantal-1]);
                            aantal--;
                        }
                    }

                    int meters = AbstracteLaag.GetInstance().AantalMeter;
                    #region alle wegen doorlopen
                    if (!kar.Vol && kar.Afstand < meters)
                    {
                        for (int i = 0; i < kar.Plaats.Kinderen.Count; i++)
                        {
                            for (int j = 0; j < wagens.Count; j++)
                            {
                                Winkelwagen g = new Winkelwagen(wagens[j]);
                                g.Afstand += kar.Plaats.Kinderen[i].Afstand;
                                g.Plaats = new POI(toestand[Kar.Plaats.Kinderen[i].Nummer]);
                                List<POI> toestandkloon = new List<POI>();
                                toestandkloon.AddRange(Toestand);
                                Oplossing kind = new Oplossing(toestandkloon, g);
                                antwoord.Add(kind);
                            }
                            
                        }
                    }
                    #endregion

                }
                return antwoord;
            }


            public List<Oplossing> kindertjes()
            {
                List<Oplossing> antwoord = new List<Oplossing>();
                //maak een poi met alle eigenschappen van zijn origineel
                POI poi = new POI(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Plaats, AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Nummer);
                for (int i = 0; i < AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen.Count; i++)
                {
                    if (!(AbstracteLaag.GetInstance().Categoriën.Contains(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen[i].Naam)))
                    {
                        poi.Artikelen.Add(AbstracteLaag.GetInstance().PuntenVanInteresse[kar.Plaats.Nummer].Artikelen[i]);
                    }
                }
                //haal hier alle ongewenste producten uit
                for (int i = poi.Artikelen.Count-1; i >= 0; i--)
                {
                    if (AbstracteLaag.Instance.Categoriën.Contains(poi.Artikelen[i].Categorie))
                    {

                    }
                    else
                    {
                        poi.Artikelen.RemoveAt(i);
                    }
                }

                int aantal = poi.Artikelen.Count;
                //als winkelwagen vol is, de maximale afstand gelopen is
                if (!kar.Vol && (kar.Afstand <= AbstracteLaag.GetInstance().AantalMeter))
                {
                    List<Winkelwagen> wagens = new List<Winkelwagen>();
                    wagens.Clear();
                    if (!kar.Plaats.Doorlopen && aantal > 0)
                    {
                        #region alle Combinaties van Artikelen worden hier gemaakt
                        double k = Math.Pow(2, aantal);
                        for (; k >= 0; k = k-1)
                        {
                            Winkelwagen tem = new Winkelwagen(kar);
                            wagens.Add(tem);
                            int plaats = (int)Math.Abs((int)Math.Pow(2, aantal) - k);
                            double temp = k;
                            for (int j = 0; j < aantal && temp != 0; j++)
                            {
                                if (temp % 2 == 1)
                                {
                                    wagens[plaats].LaadArtikelIn(poi.Artikelen[j]);
                                }
                                if (temp != 0)
                                {
                                    temp = temp / 2;
                                }
                            }
                            if (kar.Afstand == AbstracteLaag.GetInstance().AantalMeter)
                            {
                                antwoord.Add(new Oplossing(Toestand, wagens[plaats]));
                            }
                        }
                        kar.Plaats.Doorlopen = true;
                        #endregion
                    }
                    else
                    {
                        if (aantal ==0 )
                        {
                            wagens.Add(new Winkelwagen(kar));
                        }
                    }

                    int meters = AbstracteLaag.GetInstance().AantalMeter;
                    #region alle wegen doorlopen
                    if (!kar.Vol && kar.Afstand < meters)
                    {
                        for (int i = 0; i < kar.Plaats.Kinderen.Count; i++)
                        {
                            for (int j = 0; j < wagens.Count; j++)
                            {
                                Winkelwagen g = new Winkelwagen(wagens[j]);
                                g.Afstand += kar.Plaats.Kinderen[i].Afstand;
                                g.Plaats = new POI(toestand[Kar.Plaats.Kinderen[i].Nummer]);
                                List<POI> toestandkloon = new List<POI>();
                                toestandkloon.AddRange(Toestand);
                                Oplossing kind = new Oplossing(toestandkloon, g);
                                antwoord.Add(kind);
                            }
                        }
                    }
                    #endregion

                }
                return antwoord;
            }

            public Winkelwagen Kar
            {
                get { return kar; }
                set { kar = value; }
            }

            public List<POI> Toestand
            {
                get { return toestand; }
            }
        }
    }

    public class BesteTijd : Algoritme
    {
        public class Collector
        {
            private Winkelwagen bestewagen;

            public Winkelwagen Bestewagen
            {
                get { return bestewagen; }
            }

            public void GeefWinkelwagen(Winkelwagen wagen)
            {
                if (Bestewagen.Totaalprijs < wagen.Totaalprijs)
                {
                    bestewagen = wagen;
                }
            }

            private Collector()
            { }
            public static Collector GetInstance()
            {
                if (singleton == null)
                {
                    singleton = new Collector();
                }
                return singleton;
            }
            private static Collector singleton;
        }

        public Winkelwagen BesteTijdAlgoritme(int tijd, int snelheid, POI begin)
        {
            int afstand = tijd * snelheid;
            Winkelwagen wagen = new Winkelwagen(begin);
            Bereken(wagen, afstand);
            Collector col = Collector.GetInstance();
            return col.Bestewagen;
        }

        private void Bereken(Winkelwagen wagen, int afstand)
        {
            Collector collector = Collector.GetInstance();
            if (wagen.Afstand >= afstand || wagen.Vol)
            {
                collector.GeefWinkelwagen(wagen);
            }
            else
            {
                List<Winkelwagen> wagens = new List<Winkelwagen>();
                int aantal = wagen.Plaats.Artikelen.Count;
                for (int i = 0; i < Math.Pow(2, aantal); i++)
                {
                    wagens.Add(new Winkelwagen(wagen.Plaats));
                    int temp = i;
                    for (int j = 0; j < aantal; j++)
                    {
                        if (temp % 2 == 1)
                        {
                            wagens[i].LaadArtikelIn(wagen.Plaats.Artikelen[j]);
                        }
                        if (temp != 0)
                        {
                            temp = temp / 2;
                        }
                    }
                    for (int j = 0; j < wagen.Plaats.Kinderen.Count; j++)
                    {
                        wagens[i].Afstand += wagen.Plaats.Kinderen[j].Afstand;
                        AbstracteLaag ab = AbstracteLaag.GetInstance();
                        wagens[i].Plaats = ab.PuntenVanInteresse[wagen.Plaats.Kinderen[j].Nummer];
                        Bereken(wagens[i], afstand);
                    }
                }
            }
        }

        public override Winkelwagen AlgoritmeUitvoeren()
        {
            return Collector.GetInstance().Bestewagen;
        }

        public override void StopAlgoritme()
        {
            throw new NotImplementedException();
        }
    }
}
